---------------------------------------------------------------
-- @Copyright：           zour
-- @Description：         广告客户端接口函数

-- @modify time           2021-10-14
---------------------------------------------------------------
require "modules.business_advert.advert_define"
local gg = require "modules.business_advert.advert_gg"
local process_api = require "modules.business_advert.advert_process_api"
local advert_common = require "modules.business_advert.advert_common"
local b_common     = require "modules.business_common.business_common"

local FOO = {
    _VERSION = '0.1.0',
}

--获取配置 [客户端每次登录时还有切换账号时拉取一次]
FOO.ad_config_business = function()
    --客户端传参校验
    if not tonumber(ngx.ctx.m_params.uin) 
	or not tonumber(ngx.ctx.m_params.apiid) then
        log.day_list(ADVERT, "[ERROR] (process: ad_config_business) ERROR_PARAM!  uin= "
            ..(tonumber(ngx.ctx.m_params.uin) or "nil") .." channel=".. (tonumber(ngx.ctx.m_params.apiid) or "nil"))

		return gg.return_table(ERROR_CODE.OR_PARAM)
	end

    if (not ns_config.advert_change_config.config) or (not ns_config.advert_change_config.bouns) then 
        local data_body = b_common.business_nginx_share.get_nginx_share(AD_SHARE, ns_config.dict_name.config_data)
        if data_body.advert_change_config and data_body.advert_change_config.config then 
            ns_config = data_body
        else
            ns_config.fix_count = ns_config.fix_count + 1
            log.day_list(FIX_ERROR, "ad_config_business return ERROR_CODE.OR_FAIL count= ".. ns_config.fix_count)
            return gg.return_table(ERROR_CODE.OR_FAIL) 
        end
    end
    
    --根据渠道权重随机取平台ID
    local channel = tonumber(ngx.ctx.m_params.apiid)
    local platform_id = process_api.get_random_platform(channel)
    if (not platform_id) or (not ns_config.advert_change_config.config.config_channel[channel]) 
    or (not ns_config.advert_change_config.config.config_channel[channel][platform_id])  then 
        --log.day_list(ADVERT, "[ERROR]ad_config_business no platform_config! channel="..channel
            --.." platform_id="..(platform_id or "nil") )

        return gg.return_table(ERROR_CODE.OR_ERROR_PLATFORM)  
    end

    --广告位开关关了的，就不发客户端了
    local open_positions = {}
    local platform_config = ns_config.advert_change_config.config.config_channel[channel][platform_id]
    for position_id, position_info in pairs(platform_config) do
        --广告位[1为关闭]
        if tonumber(position_info.adsense_status or 0)~=1 
        and (process_api.check_ver_close(position_info.close_advert_version, ngx.ctx.m_params.ver)==false) then 
            open_positions[position_id] = position_info
        end
    end

    --send
    local ret = {}
    ret.bouns = ns_config.advert_raw_config.bouns
    ret.config = open_positions
    ret.web_update_config = ns_config.update_time

    --日志[应当放这儿只去统计正常完成的请求]
    log.day_list(AD_CONFIG, "[INFO] (process: ad_config_business) INFO_OUTPUT!  uin= "
        ..(tonumber(ngx.ctx.m_params.uin) or "nil") .." channel=".. (tonumber(ngx.ctx.m_params.apiid) or "nil"))

    return gg.return_table(ERROR_CODE.OR_SUCCESS, ret)
end

--获取广告位信息
FOO.ad_position_info_business = function()
    --客户端传参校验
    if not tonumber(ngx.ctx.m_params.uin) 
	or not tonumber(ngx.ctx.m_params.apiid)
	or not ngx.ctx.m_params.position_id     --这个接口的position是 xx_xx可以带多个广告位，其余都是1个广告位
    or not tonumber(ngx.ctx.m_params.platform_id) then
        log.day_list(ADVERT, "[ERROR] (process: ad_position_info_business) ERROR_PARAM!  uin= "
            ..(tonumber(ngx.ctx.m_params.uin) or "nil") .." channel=".. (tonumber(ngx.ctx.m_params.apiid) or "nil")
            .." position="..(ngx.ctx.m_params.position_id or "nil").." platform="..(tonumber(ngx.ctx.m_params.platform_id) or "nil"))

		return gg.return_table(ERROR_CODE.OR_PARAM)
	end

    if (not ns_config.advert_change_config.config) or (not ns_config.advert_change_config.bouns) then 
        local data_body = b_common.business_nginx_share.get_nginx_share(AD_SHARE, ns_config.dict_name.config_data)
        if data_body.advert_change_config and data_body.advert_change_config.config then 
            ns_config = data_body
        else
            ns_config.fix_count = ns_config.fix_count + 1
            log.day_list(FIX_ERROR, "ad_position_info_business return ERROR_CODE.OR_FAIL count= ".. ns_config.fix_count)
            return gg.return_table(ERROR_CODE.OR_FAIL) 
        end
    end

    local uin = tonumber(ngx.ctx.m_params.uin)
    local channel = tonumber(ngx.ctx.m_params.apiid)
    local position_ids_param = ngx.ctx.m_params.position_id
    local platform_id = tonumber(ngx.ctx.m_params.platform_id)

    --取出所有的position[该接口支持查询单个或者多个广告位] 其余接口都只传1个postion_id
    local channel_info = ns_config.advert_change_config.config.config_channel[channel] or {}
    local platform_config = channel_info[platform_id]
    if not platform_config then 
        log.day_list(ADVERT, "[ERROR]ad_position_info_business no platform_config! channel="..channel.." platform_id="..platform_id)
        return gg.return_table(ERROR_CODE.OR_ERROR_PLATFORM)
    end
    local position_t_all= {}
    for v in string.gmatch(position_ids_param, "(%d+)") do
        table.insert(position_t_all, (tonumber(v) or v ) )
    end
    if #position_t_all==0 then 
        log.day_list(ADVERT, "[ERROR]ad_position_info_business error position_ids_param="..position_ids_param)
        return gg.return_table(ERROR_CODE.OR_PARAM)
    end
    --再检测下开关，关闭状态的不发送了
    local position_t = {}
    for _, position_id in pairs(position_t_all) do
        local position_config = platform_config[position_id]
        if position_config==nil or tonumber(position_config.adsense_status or 0)~=1 then 
            table.insert(position_t, position_id )
        end
    end

    --正常开的广告位才能被获取到
    local ret_position_infos = {}
    for _, position_id in pairs(position_t) do
        local position_config = platform_config[position_id]
        if not position_config then 
            log.day_list(ADVERT, "[ERROR]ad_position_info_business no position_config! channel="..channel
                .." platform_id="..platform_id.." position_id="..position_id)
            return gg.return_table(ERROR_CODE.OR_ERROR_POSITION)
        end

        local ssdb_key_ad_info = process_api.calc_ssdb_hash_ad_info(channel, platform_id, position_id)    
        local user_config_info = gg.private_hash_get_table_ssdb(uin%10, uin, ssdb_key_ad_info)

        --检测是否需要重新加载玩家广告配置
        user_config_info = process_api.check_reload_config(user_config_info, channel, platform_id, position_id, uin)
        if not user_config_info.config.change_config.today then
            log.day_list(ADVERT, "[ERROR]ad_position_info_business user_config_info error! uin="..uin
                .." channel="..channel.." platform="..platform_id.." position="..position_id)
            return gg.return_table(ERROR_CODE.OR_GET_ADINFO_FAIL)
        end

        --获取该广告位的具体信息
        ret_position_infos[position_id] = user_config_info.config.change_config.today

        --冷却值是跟时间变化的属性，就在这里判断下发过去即可，不用存ssdb玩家数据里
        ret_position_infos[position_id].iscoding = false
        if (ret_position_infos[position_id].finish.ts + ret_position_infos[position_id].time_interval) > uu.now() then 
            ret_position_infos[position_id].iscoding = true
        end
        --把服务器配置最新时间带过去
        ret_position_infos[position_id].web_update_config = ns_config.update_time

        --若start和finish不一致，则记录下来。关于播放限制，由于可以同时获取多个广告位， \
        --当某个广告位受到限制时，返回成功还是失败呢？所以服务器给数据，客户端自己去限制
        if (ret_position_infos[position_id].start.count~=ret_position_infos[position_id].finish.count) then 
            --log.day_list(ADVERT, "ad_position_info_business count error! start_count="..ret_position_infos[position_id].start.count
                --.."  finish_count="..ret_position_infos[position_id].finish.count)
        end
    end

    --日志
    log.day_list(AD_POSITION, "[INFO] (process: ad_position_info_business) INFO_OUTPUT!  uin= "
        ..(tonumber(ngx.ctx.m_params.uin) or "nil") .." channel=".. (tonumber(ngx.ctx.m_params.apiid) or "nil")
        .." position="..(ngx.ctx.m_params.position_id or "nil").." platform="..(tonumber(ngx.ctx.m_params.platform_id) or "nil"))
    
    return gg.return_table(ERROR_CODE.OR_SUCCESS, ret_position_infos)
end

--广告位展示[逻辑是先展示曝光某个广告位，然后客户端在某一时刻才能点击播放]
--1.客户端每播放一次流程 ad_show(曝光广告位)->ad_start(点击播放)->ad_finish(播放完毕)
--2.之前旧代码为什么设计了一个ad_show曝光广告位，主要是因为当玩家正常流程下不能播放时，直接连播放广告位都不显示，\
--  若是显示了广告位却点击播放弹出播放失败信息，反而可能让玩家误以为是bug.
--3.由于每次播放都会重新调用曝光，所以其实show曝光广告位的次数即为广告播放次数，新增的start播放实际只为了检验播放时长 
--4.关于第3点，后续沟通发现客户端show后不一定会请求播放，所以show和finish次数不对应，start==finish次数
FOO.ad_show_business = function()
    --客户端传参校验
    if not tonumber(ngx.ctx.m_params.uin) 
    or not tonumber(ngx.ctx.m_params.apiid)
    or not tonumber(ngx.ctx.m_params.position_id)    
    or not tonumber(ngx.ctx.m_params.platform_id) then
        log.day_list(ADVERT, "[ERROR] (process: ad_show_business) ERROR_PARAM!  uin= "
            ..(tonumber(ngx.ctx.m_params.uin) or "nil") .." channel=".. (tonumber(ngx.ctx.m_params.apiid) or "nil")
            .." position="..(tonumber(ngx.ctx.m_params.position_id) or "nil").." platform="..(tonumber(ngx.ctx.m_params.platform_id) or "nil"))

        return gg.return_table(ERROR_CODE.OR_PARAM)
    end
 
    local uin = tonumber(ngx.ctx.m_params.uin)
    local channel = tonumber(ngx.ctx.m_params.apiid)
    local position_id = tonumber(ngx.ctx.m_params.position_id)
    local platform_id = tonumber(ngx.ctx.m_params.platform_id)

    --取玩家SSDB数据
    local code, user_config_info = process_api.get_user_data(uin, channel, platform_id, position_id)
    --客户端有一次取N个广告位的接口，若失败这里会一直无效请求show,所以在这里修正一下
    if code.ret~=ERROR_CODE.OR_SUCCESS.ret then 
        user_config_info = process_api.init_user_config(channel, platform_id, position_id)
    end

    --修改当天播放数据
    local today_ad_position_info = user_config_info.config.change_config.today
    today_ad_position_info.show.count = (today_ad_position_info.show.count or 0) + 1
    today_ad_position_info.show.ts = uu.now()
    --修改周播放数据
    local week_ad_position_info = user_config_info.config.change_config.week
    week_ad_position_info.show.count = (week_ad_position_info.show.count or 0) + 1
    week_ad_position_info.show.ts = uu.now()
    
    --回写ssdb
    local ssdb_key_ad_info = process_api.calc_ssdb_hash_ad_info(channel, platform_id, position_id) 
    local ret = gg.private_hash_set_table_ssdb(uin%10, uin, ssdb_key_ad_info, user_config_info)
    if ret~="OK" then 
        log.day_list(ADVERT, "[ERROR] ad_show_business private_hash_set_table_ssdb [ret NONE]")
    end

    --日志
    log.day_list(AD_SHOW, "[ERROR] (process: ad_show_business) INFO_OUTPUT!  uin= "
        ..(tonumber(ngx.ctx.m_params.uin) or "nil") .." channel=".. (tonumber(ngx.ctx.m_params.apiid) or "nil")
        .." position="..(tonumber(ngx.ctx.m_params.position_id) or "nil").." platform="..(tonumber(ngx.ctx.m_params.platform_id) or "nil"))

    return gg.return_table(ERROR_CODE.OR_SUCCESS)
end

--客户端点击播放并上报广告开始时间
FOO.ad_start = function()
    --客户端传参校验
    if not tonumber(ngx.ctx.m_params.uin) 
    or not tonumber(ngx.ctx.m_params.apiid)
    or not tonumber(ngx.ctx.m_params.position_id)    
    or not tonumber(ngx.ctx.m_params.platform_id) then
        log.day_list(ADVERT, "[ERROR] (process: ad_start) ERROR_PARAM!  uin= "
            ..(tonumber(ngx.ctx.m_params.uin) or "nil") .." channel=".. (tonumber(ngx.ctx.m_params.apiid) or "nil")
            .." position="..(tonumber(ngx.ctx.m_params.position_id) or "nil").." platform="..(tonumber(ngx.ctx.m_params.platform_id) or "nil"))

        return gg.return_table(ERROR_CODE.OR_PARAM)
    end

    local uin = tonumber(ngx.ctx.m_params.uin)
    local channel = tonumber(ngx.ctx.m_params.apiid)
    local position_id = tonumber(ngx.ctx.m_params.position_id)
    local platform_id = tonumber(ngx.ctx.m_params.platform_id)
    
    --取玩家SSDB数据
    local code, user_config_info = process_api.get_user_data(uin, channel, platform_id, position_id)
    if code.ret~=ERROR_CODE.OR_SUCCESS.ret then 
        log.day_list(ADVERT, "[ERROR] ad_start get_user_data fail! uin="..uin.." channel="..channel..
            " platform_id="..platform_id.." position_id="..position_id )
        user_config_info = process_api.init_user_config(channel, platform_id, position_id)
        --return gg.return_table(code)
    end

    --检测时长两次播放时长是否合法
    local user_raw_config = user_config_info.config.raw_config
    local today_ad_position_info = user_config_info.config.change_config.today
    local now_time = uu.now()
    local freq_code = process_api.check_show_freq(today_ad_position_info.start.ts, user_raw_config.ad_type, now_time)
    if freq_code.ret~=ERROR_CODE.OR_SUCCESS.ret then 
        --log.day_list(ADVERT, "[ERROR] ad_start check_show_freq fail! uin="..uin.." channel="..channel..
        --    " platform_id="..platform_id.." position_id="..position_id .." ad_type="..user_raw_config.ad_type
        --    .." start_ts="..today_ad_position_info.start.ts.." now_time="..now_time)
        return gg.return_table(freq_code)
    end

    --是否已达每日该广告位播放上限
    if today_ad_position_info.is_position_max==1 then 
        --log.day_list(ADVERT, "[ERROR] ad_start OR_POSITION_LIMIT! uin="..uin.." channel="..channel..
        --    " platform_id="..platform_id.." position_id="..position_id )
        return gg.return_table(ERROR_CODE.OR_POSITION_LIMIT)
    end

    --播放状态置为true
    user_config_info.time.isstart = true  
    --修改当天播放数据
    today_ad_position_info.start.count = (today_ad_position_info.start.count or 0) + 1
    today_ad_position_info.start.ts = uu.now()
    --修改周播放数据
    local week_ad_position_info = user_config_info.config.change_config.week
    week_ad_position_info.start.count = (week_ad_position_info.start.count or 0) + 1
    week_ad_position_info.start.ts = uu.now()
    
    --回写ssdb
    local ssdb_key_ad_info = process_api.calc_ssdb_hash_ad_info(channel, platform_id, position_id) 
    local ret = gg.private_hash_set_table_ssdb(uin%10, uin, ssdb_key_ad_info, user_config_info) 
    if ret~="OK" then 
        log.day_list(ADVERT, "[ERROR] ad_start private_hash_set_table_ssdb [ret NONE]")
    end

    --客户端和服务端配置都根据时间戳惰性更新
    local ret = {}
    ret.web_update_config = ns_config.update_time

    --日志
    log.day_list(AD_START, "[INFO] (process: ad_start) INFO_OUTPUT!  uin= "
        ..(tonumber(ngx.ctx.m_params.uin) or "nil") .." channel=".. (tonumber(ngx.ctx.m_params.apiid) or "nil")
        .." position="..(tonumber(ngx.ctx.m_params.position_id) or "nil").." platform="..(tonumber(ngx.ctx.m_params.platform_id) or "nil"))
    
    return gg.return_table(ERROR_CODE.OR_SUCCESS, ret)
end

--玩家播放广告完毕
FOO.ad_finish_business = function()
    --客户端传参校验
    if not tonumber(ngx.ctx.m_params.uin) 
    or not tonumber(ngx.ctx.m_params.apiid)
    or not tonumber(ngx.ctx.m_params.position_id)    
    or not tonumber(ngx.ctx.m_params.platform_id) then
        log.day_list(ADVERT, "[ERROR] (process: ad_start) ERROR_PARAM!  uin= "
            ..(tonumber(ngx.ctx.m_params.uin) or "nil") .." channel=".. (tonumber(ngx.ctx.m_params.apiid) or "nil")
            .." position="..(tonumber(ngx.ctx.m_params.position_id) or "nil").." platform="..(tonumber(ngx.ctx.m_params.platform_id) or "nil"))
 
        return gg.return_table(ERROR_CODE.OR_PARAM)
    end

    local uin = tonumber(ngx.ctx.m_params.uin)
    local channel = tonumber(ngx.ctx.m_params.apiid)
    local position_id = tonumber(ngx.ctx.m_params.position_id)
    local platform_id = tonumber(ngx.ctx.m_params.platform_id)

    --取玩家SSDB数据
    local code, user_config_info = process_api.get_user_data(uin, channel, platform_id, position_id)
    if code.ret~=ERROR_CODE.OR_SUCCESS.ret then 
        log.day_list(ADVERT, "[ERROR] ad_finish_business get_user_data fail! uin="..uin.." channel="..channel..
            " platform_id="..platform_id.." position_id="..position_id )
        return gg.return_table(code)
    end

    --检测播放状态
    if user_config_info.time.isstart==false then 
        --log.day_list(ADVERT, "[ERROR] ad_finish_business OR_NOT_START! uin="..uin.." channel="..channel..
        --    " platform_id="..platform_id.." position_id="..position_id )
        return gg.return_table(ERROR_CODE.OR_NOT_START)
    end

    --修改数据
    local infos = {
        channel_id = channel
    }
    process_api.user_finish_advert(user_config_info, infos)
    
    --回写ssdb
    local ssdb_key_ad_info = process_api.calc_ssdb_hash_ad_info(channel, platform_id, position_id) 
    local ret = gg.private_hash_set_table_ssdb(uin%10, uin, ssdb_key_ad_info, user_config_info) 
    if ret~="OK" then 
        log.day_list(ADVERT, "[ERROR] ad_finish_business private_hash_set_table_ssdb [ret NONE]")
        return gg.return_table(ERROR_CODE.OR_FAIL)
    end

    --这里检测提高性能
    local share_web_update_time = b_common.business_nginx_share.get_nginx_share(AD_SHARE, ns_config.dict_name.web_update_time)
    if share_web_update_time and share_web_update_time[1] and share_web_update_time[1]~=ns_config.update_time then 
        local data_body = b_common.business_nginx_share.get_nginx_share(AD_SHARE, ns_config.dict_name.config_data)
        if data_body.advert_change_config and data_body.advert_change_config.config then 
            ns_config = data_body
            log.day_list(ADVERT, "[INFO]ad_finish_business get_share_config!")
        else
            ns_config.fix_count = ns_config.fix_count + 1
            log.day_list(FIX_ERROR, "ad_finish_business return ERROR_CODE.OR_FAIL count= ".. ns_config.fix_count)
            return gg.return_table(ERROR_CODE.OR_FAIL) 
        end
    end

    --[[
    --发奖励：由于发奖每个广告位都走了不同逻辑，唯一能处理的就是2和5号类型[领取物品],故原定只迁移2和5类型的广告位发奖 \
        但是这种只处理一部分的方式反而不好。故和需求方，客户端共同决定方案为： 1.旧业务不改动 2.新需求逐个广告服新增
    --]]
    --uu.var_dump(today_ad_position_info)
    --local ret = process_api.send_rewards(today_ad_position_info)

    --由于历史原因，客户端需要返回key-value的结构
    local ret_data = {}
    ret_data[position_id] = user_config_info.config.change_config.today

    --日志
    log.day_list(AD_FINISH, "[INFO] (process: ad_finish_business) INFO_OUTPUT!  uin= "
        ..(tonumber(ngx.ctx.m_params.uin) or "nil") .." channel=".. (tonumber(ngx.ctx.m_params.apiid) or "nil")
        .." position="..(tonumber(ngx.ctx.m_params.position_id) or "nil").." platform="..(tonumber(ngx.ctx.m_params.platform_id) or "nil"))
    
    return gg.return_table(ERROR_CODE.OR_SUCCESS, ret_data)
end

--获取玩家所有数据,由于客户端会频繁请求玩家数据，所以优化为登录一次请求所有后，后续客户端自己读
FOO.ad_get_user_info = function()
    --客户端传参校验
    if not tonumber(ngx.ctx.m_params.uin) 
    or not tonumber(ngx.ctx.m_params.apiid)
    or not tonumber(ngx.ctx.m_params.platform_id) then
        log.day_list(ADVERT, "[ERROR] (process: ad_get_user_info) ERROR_PARAM!  uin= "
            ..(tonumber(ngx.ctx.m_params.uin) or "nil") .." channel=".. (tonumber(ngx.ctx.m_params.apiid) or "nil")
            .." platform="..(tonumber(ngx.ctx.m_params.platform_id) or "nil"))
 
        return gg.return_table(ERROR_CODE.OR_PARAM)
    end

    local uin = tonumber(ngx.ctx.m_params.uin)
    local channel = tonumber(ngx.ctx.m_params.apiid)
    local platform_id = tonumber(ngx.ctx.m_params.platform_id)

    local channel_info = ns_config.advert_change_config.config.config_channel[channel] or {}
    local platform_info = channel_info[platform_id]
    if not platform_info then 
        log.day_list(ADVERT, "[ERROR]ad_get_user_info no platform_config! channel="..channel
            .." platform_id="..platform_id)
        return gg.return_table(ERROR_CODE.OR_ERROR_PLATFORM)
    end

    local user_info = {}
    for position_id, v in pairs(platform_info) do

        local ssdb_key_ad_info = process_api.calc_ssdb_hash_ad_info(channel, platform_id, position_id)    
        local user_config_info = gg.private_hash_get_table_ssdb(uin%10, uin, ssdb_key_ad_info)
        --检测是否需要重新加载玩家广告配置
        user_config_info = process_api.check_reload_config(user_config_info, channel, platform_id, position_id, uin)
        if not user_config_info.config.change_config.today then
            log.day_list(ADVERT, "[ERROR]ad_position_info_business user_config_info error! uin="..uin
                .." channel="..channel.." platform="..platform_id.." position="..position_id)
        else
            user_info[position_id] = user_config_info
        end
    end

    return gg.return_table(ERROR_CODE.OR_SUCCESS, user_info)
end

--测试环境开放接口设置玩家数据
FOO.set_user_info_dev = function ()
    if ns_config.debug==1 and ngx.ctx.m_params.test then 
        local cmds = require "modules.business_advert.advert_cmds"
        cmds.set_user_info()
    end
end

--客户端埋点数据上报
FOO.ad_data_report = function()
    if not tonumber(ngx.ctx.m_params.uin) 
    or not tonumber(ngx.ctx.m_params.apiid)
    or not ngx.ctx.m_params.content then 
        return gg.return_table(ERROR_CODE.OR_FAIL)
    end
     
    local content = ngx.ctx.m_params.content
    --兼容之前没加密的客户端
    if ngx.ctx.m_params.is_base64 then 
        local base64_batchtask = uu.base64_url2std(ngx.ctx.m_params.content) or ""
        content = ngx.decode_base64(base64_batchtask) or ""
    end
    advert_common.op_business_advert(content)

    return gg.return_table(ERROR_CODE.OR_SUCCESS)
end

return FOO